package com.tianji.remark.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Primary;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author WFan
 * @since 2025-03-30
 */
@Service
@Primary
@RequiredArgsConstructor
public class LikedRecordServiceImpl3 extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final RabbitMqHelper mqHelper;
    private final RedisTemplate redisTemplate;
    // 点赞业务set结构key前缀
    private String LIKES_KEY_PREFIX = "likes:set:biz:";
    // 点赞业务zset结构key前缀
    private String LIKES_TIME_KEY_PREFIX = "likes:times:type:";

    /**
     * 点赞或取消点赞
     *
     * @param likeRecordFormDTO
     */
    @Override
    public void likeOrCancel(LikeRecordFormDTO likeRecordFormDTO) {
        String key = LIKES_KEY_PREFIX + likeRecordFormDTO.getBizId();

        // 1. 点赞新增点赞记录，取消点赞删除点赞记录
        Boolean result = likeRecordFormDTO.getLiked() ? like(key) : likeCancel(key);

        // 2. 统计点赞数
        if (!result) {
            return;
        }

        Long likeTimes = redisTemplate.opsForSet().size(key);

        // 3.缓存点赞数
        if (ObjectUtil.isNotEmpty(likeTimes)) {
            redisTemplate.opsForZSet().add(LIKES_TIME_KEY_PREFIX + likeRecordFormDTO.getBizType(),
                    likeRecordFormDTO.getBizId(), likeTimes);
        }

    }

    /**
     * 查询点赞状态
     *
     * @param bizIds
     * @return
     */

    public Set<Long> likedStatus(List<Long> bizIds) {
        if (ObjectUtil.isEmpty(bizIds)) {
            return null;
        }
        // 1.获取当前登录人
        Long userId = UserContext.getUser();

        // 2.查询点赞记录
        List pipelineResultList = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                for (Long bizId : bizIds) {
                    // 将Long转换为byte[]
                    byte[] longBytes = redisTemplate.getValueSerializer().serialize(UserContext.getUser());
                    connection.sIsMember((LIKES_KEY_PREFIX + bizId).getBytes(), longBytes);
                }
                return null;
            }
        });

        // 3.提取点赞过的业务id并返回
        // 从角标0开始，到角标size-1结束，过滤出值为true的角标，再从bizIds集合中提取出对应的值，并返回
        Set<Long> ids = IntStream.range(0, pipelineResultList.size())
                .filter(i -> (Boolean) pipelineResultList.get(i))
                .mapToObj(i -> bizIds.get(i))
                .collect(Collectors.toSet());
        return ids;
/*        Set<Long> ids = new HashSet<>(bizIds.size());
        for (int i = 0; i < pipelineResultList.size(); i++) {
            Boolean o = (Boolean) pipelineResultList.get(i);
            if (o){
                Long likeBizId = bizIds.get(i);
                ids.add(likeBizId);
            }
        }*/
    }

    /**
     * 定时检测点赞数并发送业务方持久化
     * @param bizType
     * @param maxBizSize
     */
    @Override
    public void checkLikedTimesAndSendMessage(String bizType, Integer maxBizSize) {
        // 1.批量读取redis zset中缓存的点赞总数(元组集合)  Member Score
        Set<ZSetOperations.TypedTuple<Object>>  tuples = redisTemplate.opsForZSet().popMin(LIKES_TIME_KEY_PREFIX + bizType, maxBizSize);

        // 2.数据转换
        while (ObjectUtil.isNotEmpty(tuples)){
            List<LikedTimesDTO>  messages = new ArrayList<>(tuples.size());
            for (ZSetOperations.TypedTuple<Object> tuple : tuples){
                LikedTimesDTO likedTimesDTO = new LikedTimesDTO();
                Long bizId = (Long)tuple.getValue();
                Double likeTimes = tuple.getScore();
                if (ObjectUtil.isEmpty(likeTimes) || ObjectUtil.isEmpty(bizId)){
                    continue;
                }
                likedTimesDTO.setBizId(bizId);
                likedTimesDTO.setLikedTimes(likeTimes.intValue());
                messages.add(likedTimesDTO);
            }
            if (ObjectUtil.isNotEmpty(messages)){
                // 3.发送消息
                mqHelper.send(LIKE_RECORD_EXCHANGE, StrUtil.format(LIKED_TIMES_KEY_TEMPLATE, bizType), messages);
            }

            tuples = redisTemplate.opsForZSet().popMin(LIKES_TIME_KEY_PREFIX + bizType, maxBizSize);
        }


    }

    /**
     * 取消点赞操作
     *
     * @param key
     * @return
     */
    private Boolean likeCancel(String key) {

        Long result = redisTemplate.opsForSet().remove(key, UserContext.getUser());

        return result != null && result > 0;
    }

    /**
     * 点赞操作
     *
     * @param key
     * @return
     */
    private Boolean like(String key) {
        // 1.组装key
        Long result = redisTemplate.opsForSet().add(key, UserContext.getUser());

        return result != null && result > 0;
    }
}
